﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>

//int main()
//{
//	char arr[] = "abcdefabcdef";
//	char* p = "efab";
//	char* ret = strstr(arr, p);
//
//	printf("%s\n", ret);//efabcdef
//
//	return 0;
//}
//


/********************************************************************
* strstr函数使用
功能:在str1中找str2这个字符串，第一次出现的位置
如果找到了，就返回这个第一次出现的起始地址
如果找不到，就返回NULL
*********************************************************************/
//int main()
//{
//	char arr[] = "abcdefabcdef";
//	char* p = "cdefq";
//	char* ret = strstr(arr, p);
//
//	if (ret == NULL)
//	{
//		printf("不存在\n");
//	}
//	else
//	{
//		printf("%s\n", ret);//
//	}
//	return 0;
//}
//
//
#include <assert.h>
//
////模拟strstr函数使用 

const char* my_strstr(const char* str1, const char* str2)//因为只是查询 不希望指针过程中有任何修改 用const修饰
{
	assert(str1 && str2);
	const char* s1 = NULL;//用s1和s2两个指针用来查询 用来应对多次查询情况
	const char* s2 = NULL;//赋予空指针 和str1 str2也可以 保证其起始位置
	const char* cur = str1;//用指针cur来表示 当前查询的位置 从哪开始查询 str1 = cur 目标数组 起始地址个cur 这样灵活性高 不会影响str1

	//特殊情况 - str2指向的是空字符串，直接返回str1
	if (*str2 == '\0')//查询的数组如果没有任何值 就直接返回被查询的str1
		return str1;
	while (*cur)//被查询的数组不为\0，表示里面是有字符值的 就开始循环查询
	{
		s1 = cur; //从当前位置开始查询
		s2 = str2;//s2指针被赋予了str2的起始地址
		while (*s1 && *s2 && *s1 == *s2)//s1 s2 不为\0 s1 == s2 就进循环 保证能顺利查询
		{
			s1++;//位置不断循环+1 查询两个数组是否相等
			s2++;
		}
		if (*s2 == '\0')//如果str2 查询的数组已经查完了 就直接返回到从哪个位置开始查的
			return cur;
		cur++;//如果s1与s2 有一个地方不匹配 就让从哪个地址查的++ 换一个起始位置开始s1 和 s2查 
	}
	
	return NULL;//两个数组之间确实没有存在关系 就返回空指针
}

int main()
{
	char arr[] = "abbbcf";
	char p[] = "bbc";//这样也是可以的
	//char* p = "bbc";//指针写法可能更好一些 贴合模拟strstr函数 p里面存的也是字符串的地址
	const char* ret = my_strstr(arr, p);

	if (ret == NULL)
		printf("不存在");
	else
		printf("%s\n", ret);
	return 0;
}


/**********************************************************************
*strerror 函数可以把参数部分错误码对应的错误信息的字符串地址返回来。
在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码，⼀般是放在 errno.h 这个头⽂件中说明
的，C语⾔程序启动的时候就会使⽤⼀个全局的变量errno来记录程序的当前错误码，只不过程序启动
的时候errno是0，表⽰没有错误，当我们在使⽤标准库中的函数的时候发⽣了某种错误，就会将对应
的错误码，存放在errno中，⽽⼀个错误码的数字是整数很难理解是什么意思，所以每⼀个错误码都是
有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
**********************************************************************/
//#include <string.h>
//
//int main()
//{
//	int i = 0;
//	for (i = 0; i <= 10; i++)
//	{
//		printf("%d: %s\n", i, strerror(i));
//	}
//	return 0;
//}
//

//#include <errno.h>
//
//int main()
//{
//	//打开文件
//	FILE* pf = fopen("data.txt", "r");//"r"- 读，以读文件的形式，打来文件，如果这个文件不存在，就打开失败
//	if (pf == NULL)
//	{
//		printf("打开文件失败，原因是: %s", strerror(errno));
//		return 1;
//	}
//	else
//	{
//		printf("打开文件成功\n");
//		//.....
//		fclose(pf);
//		pf = NULL;
//	}
//	return 0;
//}

/**********************************************************************
* perror -- 将error中错误对应的错误信息打印出来
* perror函数打印完参数部分的字符串后，再打印⼀个冒号和⼀个空格，再打印错误信息。
**********************************************************************/
//#include <errno.h>
//
//int main()
//{
//	//打开文件
//	FILE* pf = fopen("data.txt", "r");//"r"- 读，以读文件的形式，打来文件，如果这个文件不存在，就打开失败
//	if (pf == NULL)
//	{
//		printf("打开文件失败，原因是: %s\n", strerror(errno));
//		perror("打开文件失败，原因是");
//		//perror == printf + strerror
//		return 1;
//	}
//	else
//	{
//		printf("打开文件成功\n");
//		//.....
//		fclose(pf);
//		pf = NULL;
//	}
//	return 0;
//}
//


/*****************************************************************
* 模拟memcpy函数的实现
• 函数memcpy从 源头数组 的位置开始向后复制num个字节的数据到 目标数组 指向的内存位置。
• 这个函数在遇到 '\0' 的时候并不会停下来。
• 如果source和destination有任何的重叠，复制的结果都是未定义的。
****************************************************************/

//void* my_memcpy(void* dest,const void* src, size_t num)
//{
//	int i = 0;
//	void* ret = dest;//起始地址赋予
//	assert(dest && src);
//	for (i = 0; i < num; i++)//无论是什么类型 都是一个一个字节交换
//	{
//		*(char*)dest = *(char*)src;//将两个进行交换
//		dest = (char*)dest + 1;//交换完一个，位置加1 因为是void*类型 只能这么写才可以实现位置+1 (char*)dest++；这样写的话 下次加加就不是（char*）了 强转类型是临时的
//		src = (char*)src + 1;
//	}
//	return ret;
//}
////void* my_memcpy(void* dest, const void* src, size_t num)
////{
////	int i = 0;
////	void* ret = dest;
////	assert(dest && src);
////	while(num--)
////	{
////		*(char*)dest = *(char*)src;
////		dest = (char*)dest + 1;
////		src = (char*)src + 1;
////	}
////	return ret;
////}
////
////
////
//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[30] = { 0 };
//	
//	my_memcpy(arr2, arr1 + 2, 17);//num转几个字节
//	/*printf("%d ", arr2);*/
//	return 0;
//}

/*****************************************************************
* memmove 使⽤和模拟实现
1 void* memmove(void* destination, const void* source, size_t num);
• 和memcpy的差别就是memmove函数处理的 源内存块 和 ⽬标内存块 是可以重叠的。
• 如果源空间和⽬标空间出现重叠，就得使⽤memmove函数处理。
****************************************************************/

//void* my_memmove(void* dest, const void* src, size_t num)
//{
//	int i = 0;
//	void* ret = dest;
//	if (dest < src)//从前往后移 假设dest是目标起始位置 src是源头起始位置 dest 肯定 比src+1小 那就代表目标比源头小 就是从前往后
//	{
//		while (num--)
//		{
//			*(char*)dest = *(char*)src;
//			dest = (char*)dest + 1;
//			src = (char*)src + 1;
//		}
//	}
//	else
//	{
//		while (num--)//从后往前移
//		{
//			*((char*)dest + num) = *((char*)src + num);//num每次减一个字节 加上num直接就能到源头最后边 就是直接从后往前移了
//		}
//	}
//	return ret;
//}
//
//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	
//	my_memmove(arr1, arr1 + 2, 20);//num转几个字节 arr+2就是从元素下标2往后20个字节 就是5个整型 移到 arr1 目标数组 是可以重叠的
//	for (int i = 0; i < 10; i++)
//	{
//		printf("%d ", arr1[i]);
//	}
//	return 0;
//}

/*****************************************************************
* memset是⽤来设置内存的，将内存中的值以字节为单位设置成想要的内容。  
****************************************************************/

//int main()
//{
//	char arr[] = "hello world";
//	memset(arr+6, 'x', 5);
//	printf("%s\n", arr);
//
//	return 0;
//}


//int main()
//{
//	int arr[10] = { 0 };
//	memset(arr, 1, 4);//按字节单位来设置的 不是元素
//	//0x01 01 01 01
//	return 0;
//}

/*****************************************************************
* memcmp 函数的使⽤
1 int memcmp ( const void * ptr1, const void * ptr2, size_t num );
• ⽐较从ptr1和ptr2指针指向的位置开始，向后的num个字节
****************************************************************/
//int main()
//{
//	//0000 0000 00000000 00000000 00000001
//	//0x00 00 00 01
//	int arr1[] = { 1,2,3,4,5 };
//	//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00
//	int arr2[] = { 1,2,3,6,5 };
//	//01 00 00 00 02 00 00 00 03 00 00 00 06 00 00 00 05 00 00 00
//
//	int ret = memcmp(arr1, arr2, 13);
//
//	printf("%d\n", ret);
//	return 0;
//}
//对于整型来说，内存中存放是补码
/*****************************************************************
* ⼤⼩端字节序和字节序判断
什么是⼤⼩端？
其实超过⼀个字节的数据在内存中存储的时候，就有存储顺序的问题，按照不同的存储顺序，我们分
为⼤端字节序存储和⼩端字节序存储，下⾯是具体的概念：
⼤端（存储）模式：
是指数据的低位字节内容保存在内存的⾼地址处，⽽数据的⾼位字节内容，保存在内存的低地址处。//(倒序就是大端)
⼩端（存储）模式：
是指数据的低位字节内容保存在内存的低地址处，⽽数据的⾼位字节内容，保存在内存的⾼地址处。
****************************************************************/

//int main()
//{
//	int n = 0x11223344;//大端
//
//	return 0;
//}